﻿using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media.Animation;
using System.ComponentModel;
using System.Net;
using System.Windows.Markup;
using System.Xml;
using Lime49.UI;
using LockCrypt.Core;
using LockCrypt.Properties;
using System.IO;

namespace LockCrypt {
    /// <summary>
    /// Checks for then downloads the latest version
    /// </summary>
    public partial class WinUpdater : Window {
        private readonly WebClient client;
        private bool downloadSuccessful = false;
        private string _tempFilename = string.Empty;
        private Version latestVersion;

        /// <summary>
        /// Initializes a new <see cref="WinUpdater"/>.
        /// </summary>
        public WinUpdater() {
            InitializeComponent();
            client = new WebClient();
            client.DownloadFileCompleted += new AsyncCompletedEventHandler(client_DownloadFileCompleted);
            client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged);
        }

        /// <summary>
        /// Starts downloading when the Window loads.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void Window_Loaded(object sender, RoutedEventArgs e) {
            BackgroundWorker worker = new BackgroundWorker();
            Version currentVersion = Constants.GetVersion();
            worker.DoWork += (s, dwe) => {
                dwe.Result = Lime49.HttpRequestUtility.MakeGetRequest(Settings.Default.UpdateURL, "?app=lockcrypt&version="+currentVersion.ToString(4));
            };
            worker.RunWorkerCompleted += (s, rwe) => {
                txtProgress.Text = string.Empty;
                if(rwe.Error != null) {
                    DialogBox.ShowAlert(this, rwe.Error.Message, I18nUtils.GetString("Errors", "Error"));
                } else {
                    try {
                        string response = Convert.ToString(rwe.Result);
                        XmlDocument doc = new XmlDocument();
                        doc.LoadXml(response);
                        var lockCryptNode = doc.SelectSingleNode("/applications/application[@name='LockCrypt']");
                        if(lockCryptNode != null) {
                            var dummyNode = doc.CreateElement("dummy"); // prevent un-necessary null checks
                            latestVersion = new Version((lockCryptNode.SelectSingleNode("latest") ?? dummyNode).InnerText);
                            string linkUrl = (lockCryptNode.SelectSingleNode("link") ?? dummyNode).InnerText;
                            string moreInfoUrl = (lockCryptNode.SelectSingleNode("info") ?? dummyNode).InnerText;
                            string releaseNotes = (lockCryptNode.SelectSingleNode("notes") ?? dummyNode).InnerXml;
                            if(latestVersion > currentVersion) {
                                if(DialogBox.Show(this, I18nUtils.GetString("Strings", "VersionAvailableDownloadPromptFmt", latestVersion.ToString()), I18nUtils.GetString("Strings", "NewVersionAvailable"), DialogBox.DialogBoxType.YesNo, DialogBox.DialogBoxIcon.Question, DialogBoxButton.Yes) == DialogBoxButton.Yes) {
                                    DownloadNewVersion(linkUrl, moreInfoUrl, releaseNotes);
                                }
                            } else {
                                DialogBox.Show(this, I18nUtils.GetString("Strings", "YouHaveTheLatestVersion"), I18nUtils.GetString("Strings", "Information"), DialogBox.DialogBoxType.OK, DialogBox.DialogBoxIcon.Information, DialogBoxButton.OK);
                                DialogResult = false;
                            }
                        }
                    } catch(Exception ex) {
                        txtProgress.Text = string.Format("Invalid response: {0}", ex.Message);
                    }
                }
            };
            txtProgress.Text = I18nUtils.GetString("Strings", "Downloading");
            worker.RunWorkerAsync();
        }

        /// <summary>
        /// Starts downloading a new version.
        /// </summary>
        /// <param name="linkUrl">The URL to the file.</param>
        /// <param name="moreInfoUrl">The more info URL.</param>
        /// <param name="releaseNotes">The release notes.</param>
        private void DownloadNewVersion(string linkUrl, string moreInfoUrl, string releaseNotes) {
            try {
                _tempFilename = System.IO.Path.GetTempFileName();
                if(!string.IsNullOrEmpty(moreInfoUrl)) {
                    lnkMoreInfo.Visibility = Visibility.Visible;
                    lnkMoreInfo.NavigateUrl = moreInfoUrl;
                }
                if(!string.IsNullOrEmpty(releaseNotes)) {
                    string flowDocContents = Lime49.HtmlXaml.HtmlToXamlConverter.ConvertHtmlToXaml(releaseNotes, true);
                    FlowDocument doc = XamlReader.Parse(flowDocContents) as FlowDocument;
                    doc.FontFamily = new System.Windows.Media.FontFamily("Segoe UI");
                    txtReleaseNotes.Document = doc;
                    txtReleaseNotes.Visibility = Visibility.Visible;
                    txtReleaseNotes.BeginAnimation(FrameworkElement.HeightProperty, new DoubleAnimation(0, 250, new Duration(TimeSpan.FromMilliseconds(500))));
                }
                txtProgress.Text = I18nUtils.GetString("Strings", "Downloading");
                client.DownloadFileAsync(new Uri(linkUrl), _tempFilename);
            } catch(Exception ex) {
                txtProgress.Text = string.Format("Invalid response: {0}", ex.Message);
            }
        }

        /// <summary>
        /// Sets the DialogResult when the Window is closed.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.ComponentModel.CancelEventArgs"/> instance containing the event data.</param>
        private void Window_Closing(object sender, CancelEventArgs e) {
            DialogResult = downloadSuccessful;
        }

        /// <summary>
        /// Updates the progress bar when the progress changes.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Net.DownloadProgressChangedEventArgs"/> instance containing the event data.</param>
        private void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) {
            prgProgress.Value = e.ProgressPercentage;
            txtDownloaded.Text = Utils.PrettyFileSize(e.BytesReceived);
            txtRemaining.Text = Utils.PrettyFileSize(e.TotalBytesToReceive - e.BytesReceived);
            txtTotalSize.Text = Utils.PrettyFileSize(e.TotalBytesToReceive);
        }

        /// <summary>
        /// Sets the <c>downloadSuccessful</c> flag to <c>true</c>.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.ComponentModel.AsyncCompletedEventArgs"/> instance containing the event data.</param>
        private void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e) {
            prgProgress.Value = prgProgress.Maximum;
            if(e.Cancelled) {
                downloadSuccessful = false;
                txtProgress.Text = string.Empty;
            } else {
                downloadSuccessful = true;
                txtProgress.Text = I18nUtils.GetString("Strings", "Done");
            }
            CommandManager.InvalidateRequerySuggested();
        }

        /// <summary>
        /// Determines whether the download can be aborted.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.CanExecuteRoutedEventArgs"/> instance containing the event data.</param>
        private void CanExecuteAbort(object sender, CanExecuteRoutedEventArgs e) {
            e.CanExecute = this.IsLoaded && (client != null && client.IsBusy);
        }

        /// <summary>
        /// Aborts the download.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void AbortDownload(object sender, ExecutedRoutedEventArgs e) {
            if(client != null && client.IsBusy) {
                client.CancelAsync();
            }
        }

        /// <summary>
        /// Determines whether the window can be closed.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.CanExecuteRoutedEventArgs"/> instance containing the event data.</param>
        private void CanExecuteCloseWindow(object sender, CanExecuteRoutedEventArgs e) {
            e.CanExecute = this.IsLoaded && (client == null || !client.IsBusy);
        }

        /// <summary>
        /// Closes the window.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void CloseWindow(object sender, ExecutedRoutedEventArgs e) {
            DialogResult = downloadSuccessful;
        }

        /// <summary>
        /// Determines whether a new version was downloaded which can be installed.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.CanExecuteRoutedEventArgs"/> instance containing the event data.</param>
        private void CanExecuteInstall(object sender, CanExecuteRoutedEventArgs e) {
            e.CanExecute = IsLoaded && downloadSuccessful;
        }

        /// <summary>
        /// Launches the installer which was downloaded, then exits the application.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void LaunchInstall(object sender, ExecutedRoutedEventArgs e) {
            DialogBox.Show(this, I18nUtils.GetString("Strings", "CloseInstallNewVersion"), I18nUtils.GetString("Strings", "RestartNeeded"), DialogBox.DialogBoxType.OK, DialogBox.DialogBoxIcon.Information, DialogBoxButton.OK);
            try {
                string appDirPath = Utils.GetApplicationPath();
                string newName = Path.Combine(appDirPath, string.Format("LockCrypt-{0}-{1:MM-dd-HHmm}.zip", latestVersion == null ? string.Empty : latestVersion.ToString(), DateTime.Now));
                File.Move(_tempFilename, newName);
                Process.Start(newName);
                Application.Current.Shutdown(0);
            } catch(Exception ex) {
                DialogBox.Show(this, ex.Message, I18nUtils.GetString("Errors", "Error"));
            }
        }
    }
}
